import 'reflect-metadata';
import {ClassType} from '../type';
import {ApplicationContext} from '../context/ApplicationContext';
import DecoratorApplicationContext from '../context/support/DecoratorApplicationContext';
import {ApplicationRunner} from '../core/ApplicationRunner';
import {DirectoryUtils} from '../util/DirectoryUtils';
import {PathUtils} from '../util/PathUtils';
import * as path from 'path';

/**
 * summer application runner
 * @author Zhang
 * @date 2019/3/16 19:54
 */
export class SummerApplication {
  /**
   * 上下文
   */
  private readonly application: ApplicationContext;
  /**
   * 应用启动目录
   */
  private readonly sourcePath: string;
  /**
   * Application Runner
   */
  private readonly runner: ApplicationRunner;

  /**
   * @param clazz {@link ApplicationRunner}
   */
  private constructor(clazz: ClassType<ApplicationRunner>) {
    const runner = new clazz();
    this.runner = runner;
    this.sourcePath = PathUtils.join(runner.getBasePath(), path.sep);
    this.scanFiles();
    this.application = new DecoratorApplicationContext();
  }

  /**
   * 扫描文件
   */
  private scanFiles() {
    DirectoryUtils.readDir(this.sourcePath, (file) => {
      const modules = require(file);
      if (/\.[jt]s$/.test(file)) {
        for (const key in modules) {
          if (modules.hasOwnProperty(key)) {
            const mod = modules[key];
            this.injectNamespace(mod, file);
          }
        }
      }
    });
  }

  /**
   * 注入命名空间到模块对象
   * @param mod 模块对象
   * @param file 模块所在文件路径
   */
  private injectNamespace(mod: any, file: string) {
    const objNamespace = file
      .replace(this.sourcePath, '')
      .replace(/\.[jt]s$/, '')
      .replace(path.sep, '.');
    Reflect.defineMetadata('summer:namespace', objNamespace, mod);
  }

  /**
   * 运行程序
   * @param clazz 程序启动类
   */
  static run(clazz: ClassType<ApplicationRunner>): typeof SummerApplication {
    const summerApplication = new SummerApplication(clazz);

    return this;
  }

  /**
   * 让程序停在控制台上
   */
  static wait(): typeof SummerApplication {
    require('readline').createInterface({input: process.stdin, output: process.stdout}).question('', null);
    return this;
  }
}
